package me.ivenapp.gomoku.component;

import me.ivenapp.gomoku.*;
import me.ivenapp.gomoku.ChessPiece;
import me.ivenapp.gomoku.GomokuAI;
import me.ivenapp.gomoku.GomokuUmpire;
import ohos.agp.components.AttrHelper;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.Rect;
import ohos.app.Context;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.media.audio.SoundPlayer;
import ohos.multimodalinput.event.TouchEvent;

import java.util.ArrayList;
import java.util.List;


/**
 * 五子棋棋盘
 */
public class GomokuBoard extends Component implements Component.DrawTask, Component.EstimateSizeListener, Component.TouchEventListener {
    private static final HiLogLabel TAG = new HiLogLabel(HiLog.LOG_APP, 0x1001, GomokuBoard.class.getSimpleName());
//
    public static final int PIECE_TYPE_EMPTY = 0;
    public static final int PIECE_TYPE_WHITE = 1;
    public static final int PIECE_TYPE_BLACK = 2;
    public static final String PARAM_GAME_MODE = "PARAM_GAME_MODE";
    /**
     * 我方棋子类型
     */
    public static final String PARAM_PIECE_TYPE = "PARAM_PIECE_TYPE";
    /**
     * 对手
     */
    public static final String PARAM_OPPONENT = "PARAM_OPPONENT";
    /**
     * 人机对战
     */
    public static final int MODE_AI = 1;
    /**
     * 联机对战
     */
    public static final int MODE_ONLINE = 2;
    /**
     * 自娱自乐
     */
    public static final int MODE_SELF = 3;
    /**
     * 动作：开始游戏
     */
    public static final String ACTION_START_GAME = "ACTION_START_GAME";
    /**
     * 动作：放置棋子
     */
    public static final String ACTION_PUT_PIECE = "ACTION_PUT_PIECE";
    /**
     * 动作：悔棋
     */
    public static final String ACTION_BACK_STEP = "ACTION_BACK_STEP";
    /**
     * 动作：复盘
     */
    public static final String ACTION_REPLAY = "ACTION_REPLAY";
    /**
     * 动作：退出游戏
     */
    public static final String ACTION_EXIT = "ACTION_EXIT";

    //棋盘宽度
    private int mPanelWidth;
    //棋盘格子的大小
    private float mCellSize;
    //棋盘最大行列数
    private int rowNum = 10;
    private boolean mIsMute;

    //定义画笔绘制棋盘格子
    private Color colorAlert; //高亮颜色
    private final Paint mBoardPaint = new Paint();
    private final Paint mWhitePaint = new Paint();
    private final Paint mBlackPaint = new Paint();
    private Paint mOrderPaint; //序号的画笔
    private Paint mCursorPaint; //标示棋子的画笔

    private boolean mIsReplay; //是否是复盘状态int
    int replayIndex = 9999; //复盘棋子的索引
    private ChessPiece replayPiece; //复盘时选中的棋子(可以是临时演示棋子，也可以是已有的棋子）
    private final List<ChessPiece> mPieceList = new ArrayList<>();
    //存放棋子的矩阵。0-表示空位，1-表示白子，2-表示黑子
    private int[][] pieceArray;
    //权值数组
    private int[][] weightArray;

    private int myPieceType = PIECE_TYPE_WHITE;
    private int gameMode;
    private GomokuAI gomokuAI;
    private GomokuUmpire umpire; //裁判

    private IPutPieceListener putPieceListener;
    private IGameWinListener winListener;

    //游戏是否结束
    private boolean isGameWin;

    public GomokuBoard(Context context, AttrSet attrSet) {
        super(context, attrSet);
        // 设置测量组件的侦听器
        setEstimateSizeListener(this);
        init();
        addDrawTask(this);
        setTouchEventListener(this);
        setEnabled(false);
    }

    /**
     * 初始化设置
     */
    private void init() {
        colorAlert = new Color(getContext().getColor(ResourceTable.Color_alert));
        //棋盘格子线画笔
        mBoardPaint.setColor(new Color(Color.getIntColor("#88000000")));
        //设置抗锯齿
        mBoardPaint.setAntiAlias(true);
        //设置防抖动
        mBoardPaint.setDither(true);
        //设置为空心(画线)
        mBoardPaint.setStyle(Paint.Style.STROKE_STYLE);

        //白棋画笔
        mWhitePaint.setColor(Color.WHITE);
        mWhitePaint.setAntiAlias(true);
        mWhitePaint.setDither(true);
        mWhitePaint.setStyle(Paint.Style.FILL_STYLE);

        if(mCursorPaint == null) {
            mCursorPaint = new Paint();
        }
        mCursorPaint.setColor(colorAlert);
        mCursorPaint.setAntiAlias(true);
        mCursorPaint.setDither(true);
        mCursorPaint.setStyle(Paint.Style.FILL_STYLE);

        //黑棋画笔
        mBlackPaint.setColor(Color.BLACK);
        mBlackPaint.setAntiAlias(true);
        mBlackPaint.setDither(true);
        mBlackPaint.setStyle(Paint.Style.FILL_STYLE);
    }

    /**
     * 设置棋盘行列数。大屏15x15，手机（小屏）10x10
     * @param num
     */
    public void setRowNum(int num) {
        rowNum = num;
    }

    //截获触摸事件自行处理。获取待放置棋子。
    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        if(touchEvent.getAction() == TouchEvent.PRIMARY_POINT_UP) {
            if(pieceArray == null) {
                return true;
            }

            int x = (int) touchEvent.getPointerPosition(0).getX();
            int y = (int) touchEvent.getPointerPosition(0).getY();
            //每个格子的宽度是 mCellSize， 用x坐标除以mLineHeight取整可得出x应该落在第几个格子上
            //假设 mCellSize 为 10，x为7，
            int row = (int) (y / mCellSize);
            int col = (int) (int) (x / mCellSize);

            //超出棋盘范围
            if(row >= rowNum || col >= rowNum) {
                return true;
            }

            //复盘模式
            if(mIsReplay) {
                //防止重复点击
                if(replayPiece != null && replayPiece.getRow() == row && replayPiece.getCol() == col) {
                    return true;
                }

                if(gameMode != MODE_ONLINE) {
                    putPiece(new ChessPiece(row, col, 0));
                } if(putPieceListener != null) {
                    putPieceListener.onPutPiece(new ChessPiece(row, col, 0));
                }

                return true;
            }

            //非复盘模式--->
            if(isGameWin || pieceArray[row][col] > 0) {
                return true;
            }

            int type = myPieceType;
            if(mPieceList.size() > 0) {
                ChessPiece last = mPieceList.get(mPieceList.size()-1);
                if(gameMode == MODE_SELF) {
                    //根据最后一个棋子的颜色，确定当前要放置棋子的颜色
                    type = last.getType() == PIECE_TYPE_WHITE ? PIECE_TYPE_BLACK : PIECE_TYPE_WHITE;
                } else if(last.getType() == myPieceType) {
                    //非自娱自乐模式下，如果最后一颗是自己下的，则要等对方下了才能再下
                    return true;
                }
            }

            ChessPiece piece = new ChessPiece(row, col, type);
            if(gameMode != MODE_ONLINE) {
                //非联机模式直接放置棋子
                putPiece(piece);
            } else if(putPieceListener != null) {
                //联机模式通过数据库变化事件更新界面
                putPieceListener.onPutPiece(piece);
            }
        }
        return true;
    }

    //------动作

    /**
     * 开始游戏
     * @param mode 游戏模式
     * @param pieceType 我方棋子颜色
     */
    public void startGame(int mode, int pieceType) {
        mIsReplay = false;
        replayIndex = 9999;
        replayPiece = null;
        isGameWin = false;
        mPieceList.clear();
        mWhitePaint.setAlpha(1);
        mBlackPaint.setAlpha(1);
        invalidate();

        gameMode = mode;
        myPieceType = pieceType;
        pieceArray = new int[rowNum][rowNum];
        if(gameMode == MODE_AI) {
            gomokuAI = new GomokuAI(pieceArray);
            weightArray = gomokuAI.getWeightArray();
        }
        umpire = new GomokuUmpire(pieceArray);

        setEnabled(true);
    }

    /**
     * 悔棋
     * @param pieceType 悔棋方棋子类型
     */
    public void backStep(int pieceType) {
        if(isGameWin) return;

        if(mPieceList.size() > 0) {
            //先移除最后一颗棋子
            ChessPiece last = mPieceList.remove(mPieceList.size() - 1);
            pieceArray[last.getRow()][last.getCol()] = 0; //将该格子恢复为空子
            //如果最后一颗棋子不是悔棋方下的，就再后退一步
            if(last.getType() != pieceType && mPieceList.size() > 0) {
                last = mPieceList.remove(mPieceList.size() - 1);
                pieceArray[last.getRow()][last.getCol()] = 0;
            }

            invalidate();
        }
    }

    /**
     * 放置棋子
     */
    public void putPiece(ChessPiece piece) {
        if(piece == null) return;
        HiLog.debug(TAG, "putPiece. pieceType=" + piece.getType());

        //复盘放置棋子
        if(mIsReplay) {
            replayPiece = piece;
            invalidate();
            return;
        }

        //以下是对局中放置棋子

        if(isGameWin) return;

        //如果该位置已经有棋子了则不作处理
        if(pieceArray[piece.getRow()][piece.getCol()] > 0) {
            return;
        }

        mPieceList.add(piece);
        pieceArray[piece.getRow()][piece.getCol()] = piece.getType();
        invalidate();

        if(gameMode == MODE_AI) {
            if(piece.getType() == myPieceType) {
                playSound();
            }
        } else {
            playSound();
        }

        //棋盘下满了还未分出胜负，直接结束游戏
        if(mPieceList.size() == rowNum * rowNum) {
            isGameWin = true;
        }
        boolean isWin = umpire.isWin(piece.getRow(), piece.getCol());
        if(isWin) {
            isGameWin = true;
        }
        if(isGameWin) {
            setEnabled(false);
            if(winListener != null) {
                //0表示平局
                int winner = isWin ? piece.getType() : 0;
                winListener.onOver(winner);
            }
            return;
        }

        //人机模式下，ai开始走棋
        if(gameMode == MODE_AI) {
            updateWeight();
            if(piece.getType() == myPieceType) {
                gomokuAI.ai();
                aiPlay();
            }
        }
    }

    /**
     * 复盘
     */
    public void setReplay() {
        if(isGameWin) {
            mIsReplay = true;
            invalidate();
        }
    }

    /**
     * 设置游戏结束
     * @param winnerType 获胜棋子类型。
     */
    public void setGameWin(int winnerType){
        isGameWin = true;
        if(winListener != null) {
            winListener.onOver(winnerType);
        }
    }

    //-----end

    /**
     * 设置是否关闭音效
     * @param isMute
     */
    public void setMute(boolean isMute) {
        mIsMute = isMute;
    }

    /**
     * AI走棋
     */
    private void aiPlay(){
        if(isGameWin) return;

        //根据最后一个棋子的颜色，确定当前要放置棋子的颜色
        if(mPieceList.size() > 0) {
            ChessPiece last = mPieceList.get(mPieceList.size()-1);
            if(last.getType() != myPieceType) {
                return;
            }
        }

        int x1 = 0;
        int y1 = 0;
        int[] max= new int[weightArray.length];	//存储行最大权值
        for(int i =0 ;i<weightArray.length;i++){	//求出x方向上面的最大值
            int maxX = weightArray[i][0];
            for(int j =0;j<weightArray[i].length;j++){
                if(maxX < weightArray[i][j]){
                    maxX = weightArray[i][j];
                }
            }
            max[i] = maxX;
        }
        int m =max[0];
        for(int i=1;i<max.length;i++){
            if(m<max[i]){
                m = max[i];
            }
        }

        int flag = 0;

        for(int i =0 ;i<weightArray.length;i++){
            for(int j =0;j<weightArray[i].length;j++){
                if(weightArray[i][j] == m){	//找出最大是的行和列标
                    x1 = i;
                    y1 = j;
                    flag = 1;
                    break;
                }
            }
            if(flag == 1){
                break;
            }
        }
        ChessPiece piece = new ChessPiece();
        piece.setRow(x1);
        piece.setCol(y1);
        piece.setType(myPieceType == PIECE_TYPE_WHITE ? PIECE_TYPE_BLACK : PIECE_TYPE_WHITE);
        putPiece(piece);
    }

    private void updateWeight(){
        for(int i =0;i<weightArray.length;i++){
            for(int j = 0;j<weightArray[i].length;j++){
                weightArray[i][j] = 0;
            }
        }
    }

    private void playSound() {
        if(mIsMute) {
            return;
        }

        SoundPlayer soundPlayer = new SoundPlayer(getContext().getBundleName());
        soundPlayer.playSound(SoundPlayer.SoundType.KEY_CLICK, .3f);
    }

    @Override
    public boolean onEstimateSize(int widthEstimateConfig, int heightEstimateConfig) {
        mPanelWidth = EstimateSpec.getSize(widthEstimateConfig);
        int widthConfig = EstimateSpec.getSizeWithMode(mPanelWidth, EstimateSpec.PRECISE);
        int heightConfig = EstimateSpec.getSizeWithMode(mPanelWidth, EstimateSpec.PRECISE);

        setEstimatedSize(widthConfig, heightConfig);

        return true;
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        mPanelWidth = component.getWidth();
        mCellSize = mPanelWidth * 1.0f / rowNum;
        drawBoard(canvas);
        drawPieces(canvas);
    }

    /**
     * 绘制棋盘。棋盘四周有 mCellSize/2 的padding
     * @param
     */
    private void drawBoard(Canvas canvas) {
        int w = mPanelWidth;
        float lineHeight = mCellSize;

        for (int i = 0; i < rowNum; i++) {
            int startX = (int) (lineHeight / 2);
            int endX = (int) (w - lineHeight / 2);

            int y = (int) ((0.5 + i) * lineHeight);
            //画横线
            canvas.drawLine(startX, y, endX, y, mBoardPaint);
            //画竖线
            canvas.drawLine(y, startX, y, endX, mBoardPaint);
        }
    }

    /**
     * 绘制棋子
     */
    private void drawPieces(Canvas canvas) {
        if(mPieceList.size() == 0) {
            return;
        }

        //将棋子根据行高变化
        //棋子的缩放比例(行高的3/4)
        float pieceScaleRatio = 3 * 1.0f / 4;
        float pieceWidth = pieceScaleRatio * mCellSize;

        //复盘模式下先找出复盘棋子的索引
        if(mIsReplay && replayPiece != null) {
            for(int i=0; i<mPieceList.size(); i++) {
                ChessPiece piece = mPieceList.get(i);
                //复盘的棋子是不是已有的棋子
                if(piece.getRow() == replayPiece.getRow() && piece.getCol() == replayPiece.getCol()) {
                    replayPiece.setType(piece.getType());
                    replayIndex = i;
                }
            }
        }

        for(int i=0; i<mPieceList.size(); i++) {
            ChessPiece piece = mPieceList.get(i);
            Paint piecePaint = piece.getType() == PIECE_TYPE_WHITE ? mWhitePaint : mBlackPaint;
            piecePaint.setAlpha(1);
            if(!mIsReplay) {
                canvas.drawCircle(
                        (0.5f + piece.getCol()) * mCellSize,
                        (0.5f + piece.getRow()) * mCellSize, pieceWidth / 2, piecePaint);
            } else {
                //-->复盘模式
                if(mOrderPaint == null) {
                    mOrderPaint = new Paint();
                    mOrderPaint.setAntiAlias(true);
                    mOrderPaint.setDither(true);
                    mOrderPaint.setStyle(Paint.Style.FILL_STYLE);
                    mOrderPaint.setTextSize(AttrHelper.fp2px(12, getContext()));
                }
                mOrderPaint.setColor(piece.getType() == PIECE_TYPE_WHITE ? Color.BLACK : Color.WHITE);

                if(i > replayIndex) {
                    //把选中棋子的后续棋子降低透明度
                    piecePaint.setAlpha(0.12f);
                    mOrderPaint.setAlpha(0.5f);
                }
                //绘制棋子
                canvas.drawCircle(
                        (0.5f + piece.getCol()) * mCellSize,
                        (0.5f + piece.getRow()) * mCellSize, pieceWidth / 2, piecePaint);

                //绘制序号
                //最后一颗棋子，复盘模式下复盘的棋子和其前后的棋子用高亮颜色标出来
                if(i == mPieceList.size() - 1 || i == replayIndex || i == replayIndex -1 || i == replayIndex + 1) {
                    mOrderPaint.setColor(colorAlert);
                    mOrderPaint.setAlpha(1);
                }
                //文字大小和位置
                String order = String.valueOf(i+1);
                Rect rect = mOrderPaint.getTextBounds(order);
                float ox = (0.5f + piece.getCol()) * mCellSize;
                float oy = (0.5f + piece.getRow()) * mCellSize;
                float x = ox - rect.getWidth() * 1.0f / 2;
                float y = oy + rect.getHeight() * 1.0f / 2;

                canvas.drawText(mOrderPaint, order, x, y);
            }
        }

        //演示的棋子
        if(mIsReplay && replayPiece != null && replayPiece.getType() == 0) {
            canvas.drawCircle(
                    (0.5f + replayPiece.getCol()) * mCellSize,
                    (0.5f + replayPiece.getRow()) * mCellSize, pieceWidth / 2, mCursorPaint);
        }

        //非复盘模式下，给最后俩颗棋子做标志
        if(!mIsReplay) {
            if(mPieceList.size() > 1) {
                //绘制倒数第二颗棋子的标志(实心圆圈）
                ChessPiece lastSec = mPieceList.get(mPieceList.size() - 2);
                //圆心
                float ox = (0.5f + lastSec.getCol()) * mCellSize;
                float oy = (0.5f + lastSec.getRow()) * mCellSize;
                Paint paint = lastSec.getType() == PIECE_TYPE_WHITE ? mBlackPaint : mWhitePaint;
                canvas.drawCircle(ox, oy, 10, paint);
            }

            //绘制最后一颗棋子的标志（十字架）
            ChessPiece last = mPieceList.get(mPieceList.size() - 1);
            //圆心
            float ox = (0.5f + last.getCol()) * mCellSize;
            float oy = (0.5f + last.getRow()) * mCellSize;
            Paint paint = last.getType() == PIECE_TYPE_WHITE ? mBlackPaint : mWhitePaint;
            paint.setStrokeWidth(5);
            //横线
            float startX = ox - pieceWidth / 4;
            float endX = ox + pieceWidth / 4;
            canvas.drawLine(startX, oy, endX, oy, paint);
            //竖线
            float x = (0.5f + last.getCol()) * mCellSize;
            float startY = oy - pieceWidth / 4;
            float endY = oy + pieceWidth / 4;
            canvas.drawLine(ox, startY, ox, endY, paint);
        }
    }

    /**
     * 设置放置棋子时的监听事件
     * @param listener
     */
    public void setPutPieceListener(IPutPieceListener listener) {
        putPieceListener = listener;
    }

    public void setWinListener(IGameWinListener listener) {
        winListener = listener;
    }

    /**
     * 放置棋子放置时的监听事件
     */
    public interface IPutPieceListener {
        void onPutPiece(ChessPiece chessPiece);
    }

    public interface IGameWinListener {
        /**
         * 获胜的棋子
         * @param winner 0-表示平局，1-表示白子获胜，2-白色黑子获胜
         */
        void onOver(int winner);
    }
}
